using System;
using System.Collections;
using System.Collections.Generic;
using Sedna.Core;
using log4net;

namespace Sedna.Utility.Command.Registry
{
    /// <summary>
    /// Contains commands which could be executed in utility.
    /// </summary>
    public class DefaultCommandRegistry : ICommandRegistry
	{

		#region Fields

		private static ILog log = LogManager.GetLogger(typeof(DefaultCommandRegistry));
		private IList commands = new ArrayList();

		#endregion

		#region Properties

		public IList Commands
        {
            set { commands = value; }
		}

    	#endregion

		#region ExecuteCommand

		public CommandResult ExecuteCommand(String commandName, string[] commandParameters)
        {
            ICommand commandToExecute = null;
            foreach (ICommand command in commands)
            {
                if (command.Name.ToLower().Equals(commandName.ToLower()))
                {
                    commandToExecute = command;
                    break;
                }
            }
            return ExecuteCommand(commandToExecute, commandParameters);
        }

        public CommandResult ExecuteCommand(ICommand command, string[] commandParameters)
        {
            if (command == null)
            {
                LogInfo("Command is empty. Can't execute empty command.");
                return CommandResult.EmptyCommandResult;
            }
            return LogableRun(command, commandParameters);
        }

        #endregion

        #region DisplayHelp

        public void DisplayHelp()
        {
            foreach (ICommand command in commands)
            {
                DisplayHelp(command);
            }
        }

        public void DisplayHelp(string commandName)
        {
            ICommand commandToDisplay = null;
            foreach (ICommand command in commands)
            {
                if (command.Name.ToLower().Equals(commandName.ToLower()))
                {
                    commandToDisplay = command;
                    break;
                }
            }
            DisplayHelp(commandToDisplay);
        }

        public void DisplayHelp(ICommand command)
        {
            Console.WriteLine(String.Format("********************\r\nName:{0}\r\n----\r\nDescription:{1}\r\n********************", 
                command.Name, command.Description));
        }

        #endregion

        #region Helpers

        private CommandResult LogableRun(ICommand command, string[] commandParameters)
        {
            CommandResult result = null;
            try
            {
				LogInfo(String.Format("[Utility]: {0} started", command.Name));
                result = command.Run(commandParameters);
                return result;
            }
            catch(Exception e)
            {
                Exception ex = GetTargetException(e, typeof (SednaException));
                SednaException coreEx = ex as SednaException;
                LogError(ex ?? coreEx);
                int errorCode = CommandResult.UNKNOWN_ERROR;
                if (coreEx != null)
                {
                    errorCode = coreEx.ExceptionCode;
                    if (coreEx.ExceptionCode == SednaExceptionCodes.NoErrorCode)
                    {
                        errorCode = SednaExceptionCodes.UnknownError;
                    }
                }
                result = new CommandResult(errorCode, coreEx != null? coreEx.Message : null);
                return result;
            }
            finally
            {
                LogInfo(String.Format("[Utility]: {0} finished with result {1} ({2})", command.Name,
                    result == null? String.Empty : result.Result.ToString(),
                    result == null ? String.Empty : result.Data));
            }
        }

        private void LogInfo(string message)
        {
            if (log.IsInfoEnabled)
            {
                log.Info(message);
            }
        }

        private void LogError(Exception e)
        {
            if (log.IsErrorEnabled)
            {
                log.Error("Exception occured", e);
            }
        }

        private Exception GetTargetException(Exception exception, Type type)
        {
            if (exception == null)
            {
                return null;
            }
            if (exception.GetType().Equals(type))
            {
                return exception;
            }
            return GetTargetException(exception.InnerException, type);
        }


        #endregion
    }
}
